package com.benson.common.cron.util;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.benson.common.common.exception.MyBaselogicException;
import com.benson.common.common.util.CommonUtil;
import com.benson.common.common.util.EnumUtil;
import com.benson.common.common.util.SpringContextUtil;
import com.benson.common.cron.annotation.CronConfig;
import com.benson.common.cron.entity.Cron;
import com.benson.common.cron.entity.CronCommonConfig;
import com.benson.common.cron.entity.QuartzJobDetails;
import com.benson.common.cron.entity.enums.TriggerStateEnum;
import org.quartz.*;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 定时任务工具类
 *
 * @author zhangby
 * @date 11/12/19 6:32 pm
 */
public class QuartzUtils {
    private final static Logger logger = LoggerFactory.getLogger(QuartzUtils.class);

    /**
     * 获取配置文件，扫描包
     */
    private static CronCommonConfig cronCommonConfig = SpringContextUtil.getBean(CronCommonConfig.class);

    /**
     * 创建定时任务 定时任务创建之后默认启动状态
     *
     * @param scheduler 调度器
     * @throws Exception
     */
    public static void createScheduleJob(Scheduler scheduler, Class<? extends Job> clazz) {
        try {
            CronConfig cronConfig = clazz.getAnnotation(CronConfig.class);
            /** 判断定时任务是否设置，时间 */
            String cron = cronConfig.cron();
            // 查询数据库，是否有单独设置
            Cron dbCron = new Cron().selectList(
                    new LambdaQueryWrapper<Cron>().eq(Cron::getMark, cronConfig.value())
            ).stream().findFirst().orElse(null);
            if (cron != null || dbCron != null) {
                if (ObjectUtil.isNotNull(dbCron) && StrUtil.isNotBlank(dbCron.getCron())) {
                    cron = dbCron.getCron();
                }
                // 构建定时任务信息
                JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(cronConfig.value()).build();
                // 设置定时任务执行方式
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
                // 构建触发器trigger
                CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(cronConfig.value()).withSchedule(scheduleBuilder).build();
                scheduler.scheduleJob(jobDetail, trigger);
                /** 加载定时任务 */
                logger.info(
                        StrUtil.format("{} ({}) ：{}",
                                cronConfig.description(),
                                cronConfig.value(),
                                cron
                        )
                );
            } else {
                /** 加载定时任务 */
                logger.info(
                        StrUtil.format("{} ({}) ：{}",
                                cronConfig.description(),
                                cronConfig.value(),
                                "加载失败，未设置定时任务执行时间！"
                        )
                );
            }
        } catch (Exception e) {
            throw new MyBaselogicException("999", "创建定时任务出错：定时任务已存在！");
        }
    }

    /**
     * 根据任务名称，创建定时任务
     *
     * @param scheduler
     * @param jobName
     */
    public static void createScheduleJob(Scheduler scheduler, String jobName) {
        try {
            Set<Class<?>> classes = ClassUtil.scanPackage(cronCommonConfig.getScanPackage());
            classes.stream()
                    .filter(clazz -> ObjectUtil.isNotNull(clazz.getAnnotation(CronConfig.class)))
                    .filter(clazz -> StrUtil.isNotBlank(jobName) && jobName.equals(clazz.getAnnotation(CronConfig.class).value()))
                    .findFirst().ifPresent(item -> createScheduleJob(scheduler, (Class<? extends Job>) item));
        } catch (Exception e) {
            throw new MyBaselogicException("999", "创建定时任务出错：" + jobName);
        }
    }

    /**
     * 获取定时任务状态
     */
    public static TriggerStateEnum getScheduleStatus(Scheduler scheduler, String jobName) {
        TriggerStateEnum stateEnum = null;
        try {
            Trigger.TriggerState triggerState = scheduler.getTriggerState(TriggerKey.triggerKey(jobName));
            stateEnum = EnumUtil.initEnum(TriggerStateEnum.class, triggerState.toString());
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return stateEnum;
    }

    /**
     * 获取指定定时任务，当前的cron
     */
    public static String getScheduleCurrentCron(Scheduler scheduler, String jobName) {
        String cronExpression = null;
        try {
            Trigger trigger = scheduler.getTrigger(TriggerKey.triggerKey(jobName));
            if (ObjectUtil.isNotNull(trigger)) {
                cronExpression = JSON.parseObject(
                        JSON.toJSONString(trigger),
                        Map.class)
                        .get("cronExpression").toString();
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return cronExpression;
    }
//    /**
//     * 创建定时任务 定时任务创建之后默认启动状态
//     *
//     * @param scheduler 调度器
//     * @throws Exception
//     */
//    public static void createScheduleJob(Scheduler scheduler, QuartzKeyEnum quartzKeyEnum, String cron) {
//        String jobName = quartzKeyEnum.getValue();
//        try {
//            Class<? extends Job> graspClass = CronFactory.getGraspClass(quartzKeyEnum);
//            Optional.ofNullable(graspClass).orElseThrow(() ->
//                    new MyBaselogicException("999", quartzKeyEnum.getLabel() + ":未检查到可实现的定时任务"));
//            // 构建定时任务信息
//            JobDetail jobDetail = JobBuilder.newJob(graspClass).withIdentity(jobName).build();
//            // 设置定时任务执行方式
//            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
//            // 构建触发器trigger
//            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName).withSchedule(scheduleBuilder).build();
//            scheduler.scheduleJob(jobDetail, trigger);
//        } catch (SchedulerException e) {
//            throw new MyBaselogicException("999", "创建定时任务出错：定时任务已存在！");
//        }
//    }
//

    /**
     * 根据任务名称暂停定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public static void pauseScheduleJob(Scheduler scheduler, String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            throw new MyBaselogicException("999", "暂停定时任务出错：【" + jobName + "】" + e.getMessage());
        }
    }

    /**
     * 根据任务名称恢复定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public static void resumeScheduleJob(Scheduler scheduler, String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            throw new MyBaselogicException("999", "恢复定时任务出错：【" + jobName + "】" + e.getMessage());
        }
    }

    /**
     * 根据任务名称立即运行一次定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public static void runOnce(Scheduler scheduler, String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            throw new MyBaselogicException("999", "运行一次定时任务出错：【" + jobName + "】" + e.getMessage());

        }
    }

    /**
     * 更新定时任务
     *
     * @param scheduler 调度器
     * @throws SchedulerException
     */
    public static void updateScheduleJob(Scheduler scheduler, String jobName, String cron) {
        try {
            //获取到对应任务的触发器
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName);
            //设置定时任务执行方式
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            //重新构建任务的触发器trigger
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (ObjectUtil.isNotNull(trigger)) {
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
                //重置对应的job
                scheduler.rescheduleJob(triggerKey, trigger);
            }
        } catch (SchedulerException e) {
            throw new MyBaselogicException("999", "更新定时任务出错：【" + jobName + "】" + e.getMessage());
        }
    }

    /**
     * 根据定时任务id，刷新定时任务
     */
    public static void updateScheduleJob4CronId(Scheduler scheduler, String cronId) {
        // 根据cronId，查询定时任务
        Cron cron = new Cron().selectById(cronId);
        if (ObjectUtil.isNotNull(cron)) {
            // 查询当前定时任务
            String currentCron = getScheduleCurrentCron(scheduler, cron.getMark());
            if (StrUtil.isNotBlank(currentCron) && !currentCron.equals(cron.getCron())) {
                // 刷新定时任务，执行时间
                updateScheduleJob(scheduler, cron.getMark(), cron.getCron());
            }
            // 根据jobName 创建新的定时任务
            if (StrUtil.isBlank(currentCron)) {
                createScheduleJob(scheduler, cron.getMark());
            }
        }
    }

//    /**
//     * 更新定时任务
//     *
//     * @param scheduler 调度器
//     * @throws SchedulerException
//     */
//    public static void updateScheduleJob(Scheduler scheduler, QuartzKeyEnum quartzKeyEnum, String cron) {
//        try {
//            //获取到对应任务的触发器
//            TriggerKey triggerKey = TriggerKey.triggerKey(quartzKeyEnum.getValue());
//            //设置定时任务执行方式
//            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
//            //重新构建任务的触发器trigger
//            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
//            if (ObjectUtil.isNotNull(trigger)) {
//                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
//                //重置对应的job
//                scheduler.rescheduleJob(triggerKey, trigger);
//            }
//        } catch (SchedulerException e) {
//            throw new MyBaselogicException("999", "更新定时任务出错：【" + quartzKeyEnum.getValue() + "】" + e.getMessage());
//        }
//    }

    /**
     * 根据定时任务名称从调度器当中删除定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public static void deleteScheduleJob(Scheduler scheduler, String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.deleteJob(jobKey);
        } catch (SchedulerException e) {
            throw new MyBaselogicException("999", "删除定时任务出错：【" + jobName + "】" + e.getMessage());
        }
    }


    /**
     * @param cronExpression cron表达式
     * @param numTimes       下一(几)次运行的时间
     * @return
     */
    public static List<String> getNextExecTime(String cronExpression, Integer numTimes) {
        List<String> list = new ArrayList<>();
        CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
        try {
            cronTriggerImpl.setCronExpression(cronExpression);
        } catch (ParseException e) {
        }
        // 这个是重点，一行代码搞定
        List<Date> dates = TriggerUtils.computeFireTimes(cronTriggerImpl, null, numTimes);
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (Date date : dates) {
            list.add(dateFormat.format(date));
        }
        return list;
    }

    /**
     * @param cronExpression cron表达式
     * @return
     */
    public static List<String> getNextExecTime(String cronExpression, Date from, Date to) {
        List<String> list = new ArrayList<>();
        CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
        try {
            cronTriggerImpl.setCronExpression(cronExpression);
        } catch (ParseException e) {
        }
        // 这个是重点，一行代码搞定
        List<Date> dates = TriggerUtils.computeFireTimesBetween(cronTriggerImpl, null, from, to);
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (Date date : dates) {
            list.add(dateFormat.format(date));
        }
        return list;
    }

    /**
     * 获取定时任务详情
     *
     * @return
     */
    public static QuartzJobDetails getQuartzJobDetails(Scheduler scheduler, String jobName) {
        return CommonUtil.notEmpty(jobName).map(name -> {
            QuartzJobDetails quartzJobDetails = new QuartzJobDetails();
            try {
                TriggerKey triggerKey = TriggerKey.triggerKey(jobName);
                // 获取当前线程 cron
                String cronExpression = Optional.ofNullable(scheduler.getTrigger(triggerKey))
                        .map(trigger -> JSON.parseObject(JSON.toJSONString(trigger), Map.class).get("cronExpression").toString())
                        .orElse(null);
                // 获取线程状态
                Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
                // 获取定时任务枚举
                quartzJobDetails.setJobName(jobName)
                        .setCron(cronExpression)
                        .setTriggerStateEnum(EnumUtil.initEnum(TriggerStateEnum.class, triggerState.toString()));

            } catch (SchedulerException e) {
                e.printStackTrace();
            }
            return quartzJobDetails;
        }).orElse(null);
    }

}